package plochki;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

//More Complicated version of BaiIvan
//Uses CollisionCells (places where two Figures overlap)
//For each Collision
//Removes a Figure from a Collision if it is the easiest to move (with the least moves),
//until the collision is resolved
public class BaiGrozdan extends BaseWorker{

	//0 when the cell is empty
	//The game field, holding an index of the Figure in that cell (when positive) 
	//or the index of a Collision Cell in collionCells (when negative)
	//both indexes start from 1
	int[][] helpField;
	
	//All the collision cells
	ArrayList<CollisionCell> collisionCells;
		
	public BaiGrozdan(String inputFile) {
		super(inputFile);
		
		this.helpField = new int[500][500];
		
		this.collisionCells = new ArrayList<CollisionCell>();
		
		this.name = "BaiGrozdan";
	}
	
	public BaiGrozdan(String[] inputFigures) {
		super(inputFigures);
		
		this.helpField = new int[500][500];
		
		this.collisionCells = new ArrayList<CollisionCell>();
		
		this.name = "BaiGrozdan";
	}
	
	//Main Solving method
	public void deistvai() throws IOException {
		
		long startTime = System.currentTimeMillis();
		
		if (this.consoleInputFigures != null) {
			this.populateFiguriFromConsoleLines();
		}
		else {
			this.populateFiguriFromFile();
		}
		
		//Create Field
		this.initializeField();
		
		//Create the Help Field
		this.initializeHelpField();
		
		//Resolve all Collisions
		this.resolveCollisions();

		//Measure the time required for the algorithm to finish
		this.time = System.currentTimeMillis() - startTime;
		
		//Test the result
		//this.printFigures();
		
		//Writer the final field to a File
		//this.printFieldToFile();
		
		//Writer the final field to a File
		//this.printHelpFieldToFile();
		
		//Print the work done by the Worker
		//this.printWorkDone();
	}

	//For each Collision, find new places for the Figures in the Collision, starting from the easiest to move
	protected void resolveCollisions() {
		
		for (CollisionCell cCell : this.collisionCells) {
			//Remove a Figure until a Collision is resolved
			this.resolveCollision(cCell);
		}
	}
	
	//Remove a Figure until a Collision is resolved
	protected void resolveCollision(CollisionCell cCell) {
		
		Figura figToMove;
		Figura tempFig;
		int closestMove = 0;
		int figCount = 0;
		
		while (cCell.pile.size() > 1) {
			//Set default best Figure to move
			figToMove = cCell.pile.get(0);
			closestMove = 500;
			
			//Go over all the Figures in the Collision and select the one that is easiest to move
			for(figCount = 0; figCount < cCell.pile.size(); ++ figCount) {
				//Get the current Figure
				tempFig = cCell.pile.get(figCount);
				
				//First remove the Figure from the main Field
				this.otlepiPlochka(tempFig);
				
				//Find the best new position for the Figure
				//Figure gets updated the bestMoveDistance and bestNewPosX, bestNewPosY
				this.findBestPlaceForFigure(tempFig);
				
				//Return the Figure to the initial place
				this.addToField(tempFig);
				
				//Save the Figure which is easiest to move
				if (tempFig.bestMoveDistance < closestMove) {
					closestMove = tempFig.bestMoveDistance;
					figToMove = tempFig;
				}
			}
			//Remove the best Figure from the main Field
			this.otlepiPlochka(figToMove);
			
			//Remove best Figure from its Collision Cells
			this.removeFromAllCollisions(figToMove);
			
			//Move the Figure to the new position
			this.moveFigure(figToMove, figToMove.bestNewPosX, figToMove.bestNewPosY, figToMove.bestMoveDistance);
		}	
	}

	//Remove a Figure from all the Collisions it is part of
	private void removeFromAllCollisions(Figura fig) {
		switch(fig.figuraType) {
			case"ninetile":
			{
				this.removeFromCollision(fig ,fig.posX, fig.posY);
				this.removeFromCollision(fig, fig.posX, fig.posY-1);
				this.removeFromCollision(fig, fig.posX-1, fig.posY-1);
				this.removeFromCollision(fig, fig.posX-1, fig.posY);
				this.removeFromCollision(fig, fig.posX-1, fig.posY+1);
				this.removeFromCollision(fig, fig.posX, fig.posY+1);
				this.removeFromCollision(fig, fig.posX+1, fig.posY+1);
				this.removeFromCollision(fig, fig.posX+1, fig.posY);
				this.removeFromCollision(fig, fig.posX+1, fig.posY-1);
				break;
			}
			case"plus":
			{
				this.removeFromCollision(fig, fig.posX, fig.posY);
				this.removeFromCollision(fig, fig.posX, fig.posY-1);
				this.removeFromCollision(fig, fig.posX, fig.posY+1);
				this.removeFromCollision(fig, fig.posX-1, fig.posY);
				this.removeFromCollision(fig, fig.posX+1, fig.posY);
				break;
			}
			case"hline":
			{
				this.removeFromCollision(fig, fig.posX, fig.posY);
				this.removeFromCollision(fig, fig.posX, fig.posY-1);
				this.removeFromCollision(fig, fig.posX, fig.posY+1);
				break;
			}
			case"vline":
			{
				this.removeFromCollision(fig, fig.posX, fig.posY);
				this.removeFromCollision(fig, fig.posX-1, fig.posY);
				this.removeFromCollision(fig, fig.posX+1, fig.posY);
				break;
			}
			case"angle-ur":
			{
				this.removeFromCollision(fig, fig.posX, fig.posY);
				this.removeFromCollision(fig, fig.posX-1, fig.posY);
				this.removeFromCollision(fig, fig.posX, fig.posY+1);
				break;
			}
			case"angle-dr":
			{
				this.removeFromCollision(fig, fig.posX, fig.posY);
				this.removeFromCollision(fig, fig.posX+1, fig.posY);
				this.removeFromCollision(fig, fig.posX, fig.posY+1);
				break;
			}
			case"angle-ul":
			{
				this.removeFromCollision(fig, fig.posX, fig.posY);
				this.removeFromCollision(fig, fig.posX-1, fig.posY);
				this.removeFromCollision(fig, fig.posX, fig.posY-1);
				break;
			}
			case"angle-dl":
			{
				this.removeFromCollision(fig, fig.posX, fig.posY);
				this.removeFromCollision(fig, fig.posX+1, fig.posY);
				this.removeFromCollision(fig, fig.posX, fig.posY-1);
				break;
			}
		}		
	}

	//Remove a Figure from a ColisionCell at position posX posY
	private void removeFromCollision(Figura fig, int posX, int posY) {
		int tempValue = this.helpField[posX][posY];
		if ( tempValue < 0) {
			this.collisionCells.get(-tempValue - 1).pile.remove(fig);
		}
	}
	
	//Different approaches for different algorithms
	protected void decideWhatToDo(Figura fig, int posX, int posY, int distance) {
		//If space is found, update the best new position
		fig.bestMoveDistance = distance;
		fig.bestNewPosX = posX;
		fig.bestNewPosY = posY;
	}


	//For each figure update the Help field
	protected void initializeHelpField() {

			for(Figura fig: figuri)
			{
				//Adding a Figure to the Help Field and updating each cell
				addToHelpField(fig);
			}
		}
		
	//Fill the Help field with a Figure, and adjust affected cells
	private void addToHelpField(Figura fig) {

		switch(fig.figuraType) {
			case"ninetile":
			{
				this.adjustCell(fig ,fig.posX, fig.posY);
				this.adjustCell(fig, fig.posX, fig.posY-1);
				this.adjustCell(fig, fig.posX-1, fig.posY-1);
				this.adjustCell(fig, fig.posX-1, fig.posY);
				this.adjustCell(fig, fig.posX-1, fig.posY+1);
				this.adjustCell(fig, fig.posX, fig.posY+1);
				this.adjustCell(fig, fig.posX+1, fig.posY+1);
				this.adjustCell(fig, fig.posX+1, fig.posY);
				this.adjustCell(fig, fig.posX+1, fig.posY-1);
				break;
			}
			case"plus":
			{
				this.adjustCell(fig, fig.posX, fig.posY);
				this.adjustCell(fig, fig.posX, fig.posY-1);
				this.adjustCell(fig, fig.posX, fig.posY+1);
				this.adjustCell(fig, fig.posX-1, fig.posY);
				this.adjustCell(fig, fig.posX+1, fig.posY);
				break;
			}
			case"hline":
			{
				this.adjustCell(fig, fig.posX, fig.posY);
				this.adjustCell(fig, fig.posX, fig.posY-1);
				this.adjustCell(fig, fig.posX, fig.posY+1);
				break;
			}
			case"vline":
			{
				this.adjustCell(fig, fig.posX, fig.posY);
				this.adjustCell(fig, fig.posX-1, fig.posY);
				this.adjustCell(fig, fig.posX+1, fig.posY);
				break;
			}
			case"angle-ur":
			{
				this.adjustCell(fig, fig.posX, fig.posY);
				this.adjustCell(fig, fig.posX-1, fig.posY);
				this.adjustCell(fig, fig.posX, fig.posY+1);
				break;
			}
			case"angle-dr":
			{
				this.adjustCell(fig, fig.posX, fig.posY);
				this.adjustCell(fig, fig.posX+1, fig.posY);
				this.adjustCell(fig, fig.posX, fig.posY+1);
				break;
			}
			case"angle-ul":
			{
				this.adjustCell(fig, fig.posX, fig.posY);
				this.adjustCell(fig, fig.posX-1, fig.posY);
				this.adjustCell(fig, fig.posX, fig.posY-1);
				break;
			}
			case"angle-dl":
			{
				this.adjustCell(fig, fig.posX, fig.posY);
				this.adjustCell(fig, fig.posX+1, fig.posY);
				this.adjustCell(fig, fig.posX, fig.posY-1);
				break;
			}
		}
	}
		
	//Check a cell in the help Field and make adjustments
	//If a Cell is occupied:
	//Create a CollisionCell and save its index
	//or if a CollisionCell is already there, add to it the Figure
	private void adjustCell(Figura fig, int posX, int posY) {
		//temporary value for holding the current cell value
		int tempValue;
		
		tempValue = helpField[posX][posY];
		if (tempValue == 0) {
			helpField[posX][posY] = fig.index;
		} else if (tempValue > 0) {
			//Logging that a new CollisionCell is created
			//System.err.println("CollisionCell created: " + posX + " " + posY);
			
			//Create new CollisionCell
			this.createCollisionCell(fig,this.figuri.get(tempValue-1));
			//Set the new value to the index of the new CollisionCell but negative
			helpField[posX][posY] = -this.collisionCells.size();
		} else {
			//If CollisionCell is already created at this spot, add the new Figure to it
			this.collisionCells.get(-tempValue - 1).pile.add(fig);
		}
	}
	
	//Create a new CollisionCell and add 2 Figures to it
	private void createCollisionCell(Figura fig1, Figura fig2) {
		CollisionCell newCollision = new CollisionCell();
		newCollision.pile.add(fig1);
		newCollision.pile.add(fig2);
		
		this.collisionCells.add(newCollision);
	}

	//Print the Help field to a file -> D:/MivkiResultHelpField.txt
	protected void printHelpFieldToFile() throws IOException {
		BufferedWriter writer = new BufferedWriter(new FileWriter(new File(
				this.inputFile + "HelpFieldResult.txt")));
		
		for (int i = 0; i < this.helpField[0].length; i++) {
			for (int j = 0; j < this.helpField.length; j++) {
				if (this.helpField[i][j] == 0) writer.write("_");
				else if (this.helpField[i][j] > 0) {
					writer.write("" + helpField[i][j]);
				} else {
					writer.write("X");
				}
			}
			writer.write("\n");
		}
		writer.close();
	}

}
